// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: implementation of CImplContent.
//   $Id$



#include "stdafx.h"
#include "implcontent.h"
#include "XForms.h"
#include "XFormsApp.h"
#include "XFormsElement.h"
#include "XFormsXFormsElement.h"
#include "resource.h"
#include "DOM3Core.h"
#include "DOM3ASLS.h"
#include "Model.h"
#include "namespaces.h"
#include <Common/URI/msw/URISchemeUtils.h>
#include <Common/Internet/msw/InternetUtils.h>
#include <SchemaAnalyser.h>
#include <SchemaAnalyser_i.c>
#include "evaluator.h"

typedef CComPtr<IAnalyser> IAnalyserPtr;

extern CXFormsApp theApp;

// TODO: needs a global miscellaneous function library


HRESULT putNodeText(IXMLDOMNode * pNode, BSTR sText)
{
	ASSERT(pNode != NULL);
	HRESULT hr = S_OK;
	
	CBoundNode * pBN = CBoundNode::GetBoundNode(pNode,0,true);
	if(pBN)
	{
		pBN->setValue(sText,false);
		pBN->Release();
	}
	else
	{
		//Get the first child text node 
		CComPtr<IXMLDOMNode> pChild;
		BSTR bsText = ::SysAllocString(L"text()");
		pNode->selectSingleNode(bsText,&pChild);
		::SysFreeString(bsText);
		if(pChild)
		{
			//If there is a text child, replace its text.
			pChild->put_text(sText);
		}
		else
		{
			//if no text children exist, just do put_text,
			//	that will handle the creatino of a text child
			//	for us.
			pNode->put_text(sText);
		}
	}
	return hr;
}

inline CString doubleToString(double dDbl)
{
	CString sBuf;
	sBuf.Format("%g",dDbl);

	return sBuf;

}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CImplContent::CImplContent(CImplModel* pModel)
	:m_pModel(pModel)
	,m_pdomInitial(NULL)
	,m_pdomLive(NULL)
	,CImpl(pModel->m_pXForms)
	,m_pEvaluatorProxy(0)
	,m_hLoadEvent(0)
	,m_hLoadThread(0)
	,m_bLoadSuccessful(0)
{
/*

	I'm not convinced by this class, most of its behaviour 
	applies only to instance.  The only stuff that applies to 
	schema as well is the loading of a DOM document.  In addition
	the new schema element is in a different namespace, so should 
	be handled mostly outside the scope of XForms.
*/

}

CImplContent::~CImplContent()
{

	if(m_pdomInitial) {
		delete m_pdomInitial;
		m_pdomInitial = 0;
	}
//probably should terminate the GetContentNow thread, just in case?
	if(m_pdomLive) {
		if(m_pModel)
			m_pModel->RemoveDocumentFromLookupTable(m_pdomLive);
		delete m_pdomLive;
		m_pdomLive = 0;
	}
	
	// CComObject doesn't handle the refcount we 
	// we need to release it.
	if(m_pEvaluatorProxy)
		m_pEvaluatorProxy->Release();
}

#define MESSAGE_PUMP_IN_THREADGETCONTENT 1

#ifdef MESSAGE_PUMP_IN_THREADGETCONTENT
const UINT g_msgThreadGetContent = WM_APP + 1;

DWORD WINAPI ThreadGetContent(LPVOID vdArg)
{
	HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    MSG msg;
	// force msg-q to be created just in case, NOP otherwise
	PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE | PM_NOYIELD);
	// msg to myself to do work
	PostThreadMessage(GetCurrentThreadId(),g_msgThreadGetContent, 0, 0);

	// msg-pump
	while (GetMessage(&msg, NULL, 0, 0))
	{
		// this was my message -- time to do my work
		if (msg.hwnd == NULL && msg.message == g_msgThreadGetContent)
		{
			CImplContent * pContent = reinterpret_cast<CImplContent*>(vdArg);
			HRESULT hr = pContent->GetContentNow();
			SetEvent(pContent->m_hLoadEvent);
	        PostQuitMessage(0); 
  			::CoUninitialize();
			ExitThread(hr);
	    }
	    else
        {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
        }
    }
	::CoUninitialize();
	ExitThread(hr);
	return 0;
}
#else
DWORD WINAPI ThreadGetContent(LPVOID vdArg)
{
	HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
	if(SUCCEEDED(hr))
	{
		CImplContent * pContent = reinterpret_cast<CImplContent*>(vdArg);
		hr = pContent->GetContentNow();
		SetEvent(pContent->m_hLoadEvent);
		::CoUninitialize();
	}
	ExitThread(hr);
}
#endif

HRESULT CImplContent::GetContent()
{
	HRESULT hr = S_OK;
	m_hLoadEvent = CreateEvent( 
            NULL,     // no security attributes
            TRUE,    // don't auto-reset event
            FALSE,     // initial state is unsignaled
            NULL);    // object not named
#ifdef GET_CONTENT_IN_THREAD
	DWORD IDThread;
	m_hLoadThread = CreateThread(NULL, 0, 
    (LPTHREAD_START_ROUTINE) ThreadGetContent, 
	this,
    0, &IDThread); 
#else
		hr = GetContentNow();
		SetEvent(m_hLoadEvent);
#endif	
	return hr;
}

HRESULT CImplContent::GetContentNow()
{
	HRESULT hr = S_OK;
	
	CDocumentLS* pXMLDoc = new CDocumentLS();
	m_bLoadSuccessful= false;

	{
		CComQIPtr<IXMLDOMDocument2> pDoc2;
		pDoc2 = pXMLDoc->m_pXMLParser;
		if(pDoc2)
			pDoc2->putref_schemas(CComVariant(m_pSchemaCache));
	}

	if (m_sHref)
	{
	   // If there is an XLink reference then load the document referred to
		CComBSTR sURL;

		// Bug 1009: Allow cookie: and webapp: protocols in xf:instance and xf:submission.
		char *sResolved = 0;
		CComBSTR sUnresolved;
		xport::ResolveScheme(CW2A(m_sHref), &sResolved, CW2A(m_pXForms->m_sCurrentURL), true);
		if(sResolved) {
			sUnresolved = m_sHref;
			sURL = CA2W(sResolved);
			free(sResolved);
		} else
			sURL.Attach(Relative2AbsolutePath(m_pXForms->m_sCurrentURL, m_sHref));
		m_bLoadSuccessful = false;

		pXMLDoc->m_pXMLParser->put_async(VARIANT_FALSE);
		pXMLDoc->m_pXMLParser->put_validateOnParse(VARIANT_FALSE);
		//IXMLDOMDocument::load Does not fetch from web cache.  Use wininet first, if all is well, this will 
		//	fetch from cache if cached, or run off to the interweb to get it.
		if(sURL == wcsstr(sURL,L"http://"))
		{
			void *pData = 0;
			DWORD dwSize = 0;
			char * pcURL = ::BSTRToStr(sURL);
			xport::ReadFromInternet(pcURL,&pData,dwSize);
			delete [] pcURL;
			CComBSTR bsData = reinterpret_cast<char *>(pData);
			VARIANT_BOOL vtSuccess;
			pXMLDoc->m_pXMLParser->loadXML(bsData,&vtSuccess);
			delete [] pData;
			m_bLoadSuccessful = (vtSuccess == VARIANT_TRUE);
		}
		//Fallback, in case ReadFromInternet returns gobbledegook (e.g. unexpected charset)
		//	Instead of writing several lines to try to decode it all, just let MSXML do it.
		if(!m_bLoadSuccessful)
		{
			m_bLoadSuccessful = (pXMLDoc->load(sURL)!=FALSE);
		}


		if(sUnresolved)
			sURL = sUnresolved;

		if(!m_bLoadSuccessful)
		{
			CStringW sMessage;
			sMessage.Format(L"could not connect to %s\n\t%%s\n\t at line:%%i",sURL);
			long lResponse = IDRETRY;

			while(!m_bLoadSuccessful && lResponse == IDRETRY)
			{
				CComPtr<IXMLDOMParseError> pPE;
				pXMLDoc->m_pXMLParser->get_parseError(&pPE);
				CStringW sThisMessage;
				if(pPE)
				{
					CComBSTR sReason;
					pPE->get_reason(&sReason);

					long lLine;
					pPE->get_line(&lLine);
					sThisMessage.Format(sMessage,sReason,lLine);
				}
				else
				{
					sThisMessage.Format(sMessage,0,0);
				}

				lResponse = ::MessageBoxW(0,sThisMessage ,EVENT_LINKEXCEPT,MB_RETRYCANCEL);
				if(lResponse == IDRETRY)
				{
					m_bLoadSuccessful = (pXMLDoc->load(sURL)!=FALSE);
				}
				else
				{/*
					//Fire traversalError on fail
					m_pModel->onLinkException(sURL);				
					*/
				}
			}
			
		}
	}
	else if(m_sContent)
	{
		//load from inline content in the absence of the HREF attribute.
		m_bLoadSuccessful = (pXMLDoc->loadXML(m_sContent)!=FALSE);
	}
	else if(m_sDocumentElement && m_pModel->m_pSchemaCache)
	{
		//No link and nothing inline, we do have schemata,
		// so use that to create a dummy document.
		IAnalyserPtr pAnalyser;
		
		hr = pAnalyser.CoCreateInstance(CLSID_Analyser);
		if(SUCCEEDED(hr))
		{
			//We can't get away with just using the XML of the schema, as it could have
			//	include directives.  We have to load it again through a filestream of some kind
			//hr = pAnalyser->loadXML(m_pModel->m_pSchema->m_sTargetURI.m_str,sXML.m_str);
			try
			{
				CComBSTR sNSURI;
				hr = m_pModel->m_pSchemaCache->get_namespaceURI(0,&sNSURI);
				if(SUCCEEDED(hr))
				{

					CString sSchemaURLs(m_pModel->m_sSchemaURLs);
					
					int iSpace = sSchemaURLs.Find(' ');
					if(iSpace == -1)
					{
						CComBSTR bs;
						bs.Attach(::Relative2AbsolutePath(m_pXForms->m_sCurrentURL,m_pModel->m_sSchemaURLs));
						pAnalyser->load(sNSURI.m_str,bs.m_str);
					}
					else
					{
						CString sSchemaURL = sSchemaURLs.Left(iSpace);
						sSchemaURL.Remove(' ');
						BSTR s = sSchemaURL.AllocSysString();
						CComBSTR bs;
						bs.Attach(::Relative2AbsolutePath(m_pXForms->m_sCurrentURL, s));
						pAnalyser->load(sNSURI.m_str, bs.m_str);
						::SysFreeString(s);
					}
					

					CComPtr<IDispatch> pDisp;
					pAnalyser->createEmptyDocument(m_sDocumentElement.m_str,&pDisp);
					if(pDisp)
					{
						pXMLDoc->m_pXMLParser.Release();
						hr = pDisp->QueryInterface(IID_IXMLDOMDocument2,(void**)&pXMLDoc->m_pXMLParser);
						if(SUCCEEDED(hr))
						{
							m_bLoadSuccessful = true;
						}
						else
						{
							m_bLoadSuccessful = false;
						}

					}
				}
			}
			catch(_com_error)
			{
				m_bLoadSuccessful=false;
				hr = E_FAIL;
			}

		}

	}
	else
	{
		//No other method for creating documents has succeeded,
		//	documents should be created using the  lazy programmer
		//	method defined in the spec.
	}
	
	
	if(m_bLoadSuccessful)
	{
		//	delete the current m_pdomInitial for reuse.
		if(m_pdomInitial)
			delete m_pdomInitial;
		m_pdomInitial = pXMLDoc;	

		//reset clones m_pdomInitial into m_pdomLive.
		hr = reset();

	}
	else
	{
		pXMLDoc->m_pXMLParser->get_parseError(&m_pLastError);
		if(m_pdomInitial)
			delete m_pdomInitial;
		m_pdomInitial = NULL;		
		delete pXMLDoc;
		hr = E_FAIL;
	}


	return hr;
}


HRESULT CImplContent::reset()
{
	HRESULT hr = E_FAIL;

	// Copy the 'initial' instance DOM to the 'live' 
	//	instance DOM, after deleting the current 'live'
	// one
	if(m_pdomInitial)
	{
		if(m_pdomLive)
		{
			if(m_pModel)
				m_pModel->RemoveDocumentFromLookupTable(m_pdomLive);
			delete(m_pdomLive);
		}
		//create a new Doc to keep the clone in.
		

		CComPtr<IXMLDOMNode> pN;
		hr = m_pdomInitial->m_pXMLParser->cloneNode(VARIANT_TRUE,&pN);
		CComQIPtr<IXMLDOMDocument> pDoc = pN;
		if(pDoc)
		{
			m_pdomLive = new CDOM3Document(pDoc);
		}

		onReset();
	}
	return hr;
}

HRESULT CImplContent::getXPathResult(BSTR sXPath, CNode* pContextNode, EXPathResultType eType,IXPathResult ** ppResult)
{
	//This should now be the only interface between the XML (DOM3XPath)
	//	and the form.

#if DEBUG_LEVEL & DEBUG_MONITOR_DATAACCESS
	AtlTrace("'formsplayer.dll': getXPathResult Called: \n\ttype=%d\n\t%S\n",eType,sXPath);
#endif

	//Make sure this isn't called with an empty string.
	ASSERT(sXPath != NULL);

	HRESULT hr = E_FAIL;
	*ppResult = NULL;
	//if there is no supplied context, take the root.
	//CHANGED: it is not the root that is default contaxt,
	//	it is the documentElement

	//only run if context node exists.
	if(pContextNode)
	{
#ifdef _DEBUG
		if(false)
		{
			CComBSTR bs;
			pContextNode->get_xml(&bs);
			::MessageBoxW(0,bs,0,0);
		}
#endif
		m_pXForms->PushCurrentXPathContext(pContextNode,m_pModel);

		//Unfortunately for now, we can't run this at a higher level 
		//	theApp.m_pEvaluator->evaluate(...)
		//	as we need to use addFunctionLibrary.
		//	addFunctionLibrary can only be run after expression
		//	has been created (a process entirely hidden within evaluator::evaluate).
		//	what we need here is a wrapper, at least until I have implemented
		//	a list to contain the handlers at expr.

		
		//slightly wrong - we only have one NS resolver per document, 
		//	rather than creating one in the context for the expression
		CComPtr<IXPathNSResolver> pResolver;
		m_pXForms->GetNSResolver(&pResolver);
		if(pResolver)
		{
			CComQIPtr<IXPathExpression> pExpr;
			CMapCComBSTRToXPathExpression::iterator i = m_ExprMap.find(sXPath);
			if(i != m_ExprMap.end())
			{
				pExpr = i->second;
				hr = S_OK;
			}
			else
			{
				hr = theApp.m_pEvaluator->createExpression(sXPath, pResolver, &pExpr);
				if(SUCCEEDED(hr)) 
				{
					m_ExprMap.insert(CPairCComBSTRToXPathExpression(sXPath,pExpr));
				}
			}

			if(SUCCEEDED(hr) && pExpr)
			{
				//prepare Expr with the XForms function library
				CComPtr<IDispatch> pFuncLib;
				hr = m_pXForms->getFunctionLibrary(0,&pFuncLib);
				
				if(SUCCEEDED(hr))
				{
					pExpr->addFunctionLibrary(pFuncLib,theApp.m_sXFormsURI,TRUE);

					// HACK: Use this namespace to circumnavigate the Novell XForms implementation.
					//pExpr->addFunctionLibrary(pFuncLib, theApp.m_sXFormsURI_NovellHack, FALSE);

					CComPtr<IHTMLDocument2> pDoc;
					m_pXForms->GetOwnerDocument(&pDoc);
					if(pDoc) {
						CComPtr<IDispatch> pDisp;
						pDoc->get_Script(&pDisp);
						if(pDisp)
							pExpr->addFunctionLibrary(pDisp,::g_bsNSInlineScript);
					}
				}
				//TODO: make an HRESULT for this.
				else
					m_pXForms->reportException("formsplayer default function library has failed to load.");
				
				CMapCComBSTRToIDispatchPtr::iterator i = m_pModel->m_Libraries.begin();
				while(i != m_pModel->m_Libraries.end())
				{
					BSTR s = i->first;
					CComQIPtr<IDispatch> pDisp(i->second);
					pExpr->addFunctionLibrary(pDisp,s);
					++i;
				}

				
				//execute the expression.
				hr = pExpr->evaluate(pContextNode, eType, NULL,ppResult);
				//HACK: sometimes this gets confused - some kind of synchronisation feature should be introduced
				if(hr == E_PENDING)
				{
					hr = pExpr->evaluate(pContextNode, eType, NULL,ppResult);
				}
				//remove the functionlibraries
				pExpr->addFunctionLibrary(NULL,theApp.m_sXFormsURI);
				pExpr->addFunctionLibrary(0,::g_bsNSInlineScript);

				// HACK: We only used this namespace to circumnavigate the Novell XForms implementation.
				//pExpr->addFunctionLibrary(0, theApp.m_sXFormsURI_NovellHack);


				//pos = m_pModel->m_Libraries.GetStartPosition();
				try
				{
					if(m_pModel)
					{
						i = m_pModel->m_Libraries.begin();
						while(i != m_pModel->m_Libraries.end())
						{
							pExpr->addFunctionLibrary(0,i->first);
							++i;
						}
					}
				}
				catch(...)
				{
					//m_pModel or this, may have been deleted during expr eval
					hr = E_UNEXPECTED;

				}

			}
			else if(FAILED(hr) && m_pXForms->canContinue())
			{
				// in the case of an error, report it and fire the 
				//	xforms-binding-exception event (WD version: 21 August 2002)
				
				//	This error is a parse-time error, and would be due to gammy
				//	syntax in your XPath statement.
				// this error can only be due to a syntax error, not a
				//	dodgy function like below.
				m_pModel->onBindingException(sXPath,L"XPath syntax error");
			}
		}
		if(FAILED(hr) && m_pXForms->canContinue() && m_pModel)
		{
			// in the case of an error, report it and fire the 
			//	xforms-binding-exception event (WD version: 21 August 2002)
			
			//	This error is a parse-time error, and would be due to gammy
			//	syntax in your XPath statement.
			//	or it may be a run-time error caused by trying to call a non-existant
			//	function.
			
			//If a fatal error has already been encountered, this is probably not an
			//	appropriate error.  If we have got this far, then the fatal error would
			//	have occured within an XForms function called by the XPath Expression,
			//	either by a dodgy bind with getInstanceDocument, or by incompatible 
			//	arguments sent to the functions (xforms-compute-exception)(not implemented). 
			m_pModel->onBindingException(sXPath,L"XPath syntax error, or function does not exist"); 
		}
		try
		{
			m_pXForms->PopCurrentXPathContext();
		}
		catch(...)
		{
			//this may have been deleted
			hr = E_UNEXPECTED;
		}

	}



#if DEBUG_LEVEL & DEBUG_MONITOR_DATAACCESS
	AtlTrace("'formsplayer.dll' getXPathResult Done.\n",eType,sXPath);
#endif

	return hr;
}

HRESULT CImplContent::getInstanceNode(BSTR sXPath, CNode* pContextNode, CNode** ppNode)
{
	HRESULT hr = S_OK;

	CComPtr<IXPathResult> pResult;
	//Get the IXPathResult object based on required XPath and Result type.
	hr = getXPathResult
	(
		sXPath,
		pContextNode,
		FIRST_ORDERED_NODE_TYPE,
		&pResult
	);
	//Get the node and the HRESULT for returning
	if(SUCCEEDED(hr))
	{
		hr = pResult->get_singleNodeValue(ppNode);
	}
	return hr;
}


HRESULT CImplContent::getInstanceNodeList(BSTR sXPath, CNode* pContextNode, IXPathResult** ppResult)
{
	//Get the IXPathResult object based on required XPath and Result type.
	return getXPathResult
	(
		sXPath,
		pContextNode,
		ORDERED_NODE_SNAPSHOT_TYPE,
		ppResult
	);
}

HRESULT CImplContent::getInstanceData(BSTR sXPath,CNode* pContextNode, IXPathResult** ppData)
{
	//Gets the most appropriate data (node, string etc.) from the XPATH provided
	return getXPathResult
	(
		sXPath,
		pContextNode,
		ANY_TYPE,
		ppData
	);
}

HRESULT CImplContent::getInstanceNumber(BSTR sXPath,CNode* pContextNode, double* pdData)
{

	CComPtr<IXPathResult> pResult;
	//Get the IXPathResult object based on required XPath and Result type.
	HRESULT hr = getXPathResult
	(
		sXPath,
		pContextNode,
		NUMBER_TYPE,
		&pResult
	);
	
	//Get the node and the HRESULT for returning
	if(SUCCEEDED(hr))
		hr = pResult->get_numberValue(pdData);
	return hr;
}

HRESULT CImplContent::getInstanceString(BSTR sXPath, CNode* pContextNode, BSTR* psData)
{
	HRESULT hr;

	CComPtr<IXPathResult> pResult;
	//Get the IXPathResult object based on required XPath and Result type.
	hr = getXPathResult
	(
		sXPath,
		pContextNode,
		STRING_TYPE,
		&pResult
	);
	
	//Get the node and the HRESULT for returning
	// Need to check pResult can be a null pointer.
	if(SUCCEEDED(hr) && pResult)
		hr = pResult->get_stringValue(psData);
		
	return hr;
}

HRESULT CImplContent::putInstanceString(BSTR sXPath, CNode* pContextNode, BSTR sData)
{
#if DEBUG_LEVEL & DEBUG_MONITOR_DATAACCESS
	AtlTrace("'formsplayer.dll':putInstanceString Called\n");
#endif
	HRESULT hr = S_OK;

	
	CComPtr<IXMLDOMNode> pNode;
	
		//get the node to put it in.
	hr = getInstanceNode(sXPath, pContextNode, &pNode);
	if(SUCCEEDED(hr))
		hr = putInstanceString(pNode,sData);
	return hr;
}
HRESULT CImplContent::putInstanceString(CNode*pNode, BSTR sData)
{
	HRESULT hr;
	if (pNode)
	{
		
		putNodeText(pNode, sData);
		hr = S_OK;
		//TODO: create or find a vertex 
		//	for the affected node, and make
		//	that vertex pertinent.
		//	
		//for now, just ask the model
		//	to flag the MDL for recalc.
		m_pModel->makePertinent(0);


		/*
		 * [TODO]
		 * It's possible for script to attach an event
		 *		to a particular instance node, to watch for
		 *		a change. This is where we would need to 
		 *		fire that event.
		 */

	}
	else
	{
		hr = E_INVALIDARG;
	}
#if DEBUG_LEVEL & DEBUG_MONITOR_DATAACCESS
	AtlTrace("\n 'formsplayer.dll':putInstanceString Done");
#endif
	return hr;
}

HRESULT CImplContent::putInstanceData(CNode * pRecipientNode,IXPathResult * pData)
{
#if DEBUG_LEVEL & DEBUG_MONITOR_DATAACCESS
	AtlTrace("'formsplayer.dll': putInstanceData Called\n");
#endif
	HRESULT hr = S_OK;
	short sType;
	pData->get_resultType(&sType);
	switch(sType)
	{
		case BOOLEAN_TYPE:
			{
				BOOL bData;
				pData->get_booleanValue(&bData);
				hr = putInstanceString(pRecipientNode, CComBSTR(bData ? L"true" : L"false"));
			}
			break;
		case NUMBER_TYPE:
		{
			//get the numeric value.
			double dbl;
			pData->get_numberValue(&dbl);
			//convert it to a string. WARNING: untested.
			CComBSTR bsBuf(::doubleToString(dbl));
			hr =  putInstanceString(pRecipientNode,bsBuf);
			break;
		}
		case STRING_TYPE:
		{
			CComBSTR sData;
			pData->get_stringValue(&sData);
			hr = putInstanceString(pRecipientNode, sData);
			break;
		}
		case UNORDERED_NODE_ITERATOR_TYPE:
		case ORDERED_NODE_ITERATOR_TYPE:
		case FIRST_ORDERED_NODE_TYPE:
		{
			CComQIPtr<IXMLDOMNode> pNode = pRecipientNode;
			//get the target node
			if(pNode)
			{
				//Find out the node type. of the target node
				DOMNodeType nt;
				pNode->get_nodeType(&nt);

				//get the source node
				CComPtr<IXMLDOMNode> pSrcNode;
				if(FIRST_ORDERED_NODE_TYPE==sType)
					pData->get_singleNodeValue(&pSrcNode);
				else
					//apply first-node rule.
					pData->iterateNext(&pSrcNode);

				//An empty collection will return an iterator type,
				//	so check if we actually have a node to copy out of
				if(pSrcNode)
				{
					switch(nt)
					{
						case NODE_ELEMENT:
						{
							//Work out whether this is a textual put,
							//	or a tree put.
							bool bIsTextPut = false;
							CComPtr<IXMLDOMNode> pN;
							pSrcNode->get_firstChild(&pN);
							if(pN)
							{
								DOMNodeType type;
								pN->get_nodeType(&type);
								if(type == NODE_TEXT)
								{
									//The first child is a text node, see
									//	if it is also the last child.
									CComPtr<IXMLDOMNode> pN2;
									pSrcNode->get_lastChild(&pN2);
									if(pN.IsEqualObject(pN2))
									{
										//if the source has only one node
										//	 and that is a text node, then
										//	we should put text.
										bIsTextPut = true;


									}
								}
							}
							
							if(bIsTextPut)
							{
								CComBSTR s;
								pSrcNode->get_text(&s);
								putNodeText(pNode, s);

								//TODO: create or find a vertex 
								//	for the affected node, and make
								//	that vertex pertinent.
								//	
								//for now, just ask the model
								//	to flag the MDL for recalc.
								m_pModel->makePertinent(0);
							}
							else
							{
								//create a shallow clone of the original node and replace
								//	the old one with it. This throws away any existing content
								CComPtr<IXMLDOMNode> pTarg;

								if(SUCCEEDED(pNode->cloneNode(VARIANT_FALSE,&pTarg)))
								{
									CComPtr<IXMLDOMNode> pParent;
									hr = pNode->get_parentNode(&pParent);
									if(hr == S_OK && SUCCEEDED(pParent->replaceChild(pTarg,pNode,NULL)))
									{
										//get the children of the source node and
										//	append them to the target node
										CComPtr<IXMLDOMNodeList> pSrcNl;
										CComPtr<IXMLDOMNode> pNIn;

										pSrcNode->get_childNodes(&pSrcNl);
										pSrcNl->nextNode(&pNIn);

										IXMLDOMNode *pNClone = 0;
										while(pNIn) {
											// Clone the child node.
											hr = pNIn->cloneNode(VARIANT_TRUE,&pNClone);

											// Release the child node - done here to ensure the loop terminates in error scenarios [bug 1070].
											pNIn.Release();

											if(SUCCEEDED(hr))
											{
												// Bug 1070: Temporarily remove the xsi:type attribute, because MSXML is gay.
												if(m_pModel)
													m_pModel->removeXSITypeAttributes(pNClone);

												// Append the cloned node to the target.
												hr = pTarg->appendChild(pNClone,&pNClone);

												// Bug 1070: Reinstate the xsi:type attribute that was removed because of MSXML's gayness quotient.
												if(m_pModel)
													m_pModel->replaceXSITypeAttributes(pNClone);

												// Iterate.
												if(SUCCEEDED(hr))
													pSrcNl->nextNode(&pNIn);
											}
											::safeRelease(pNClone);
											pNClone = 0;
										}
									}
								}
								m_pModel->rebuild(m_pXForms->IsInDeferredUpdate());
							}
							break;
						}
						case NODE_DOCUMENT :
						{
							//if a document, copy the new node into it as the documentElement
							CComPtr<IXMLDOMNodeList> pNl;
							pNode->get_childNodes(&pNl);
							if(pNl) {
								CComPtr<IXMLDOMNode> pLoser;
								pNl->nextNode(&pLoser);
								CComPtr<IXMLDOMNode> pLost;
								while(pLoser)
								{
									pNode->removeChild(pLoser,&pLost);
									pNl->nextNode(&pLoser);
								}
								pNode->appendChild(pSrcNode,NULL);
								m_pModel->rebuild(m_pXForms->IsInDeferredUpdate());
							}
							break;
						}
						case NODE_TEXT :
						case NODE_ATTRIBUTE:
						{
							//Attributes and text can only take text.
							CComBSTR sText;
							pSrcNode->get_text(&sText);
							pNode->put_text(sText);
							sText.Empty();
							break;
						}
					}
				}
			}
			break;
		}
		default:
		{
			hr = S_FALSE;
		}
	}
#if DEBUG_LEVEL & DEBUG_MONITOR_DATAACCESS
	AtlTrace("'formsplayer.dll': putInstanceData Done\n");
#endif
	return hr;
}
#define WAIT_FOR_LOADEVENT_WITH_MESSAGELOOP 1

CDOM3Document * CImplContent::GetLiveDOM()
{
	if(m_hLoadEvent)
	{
		#ifdef WAIT_FOR_LOADEVENT_WITH_MESSAGELOOP
			xport::WaitWithMessageLoop(m_hLoadEvent,INFINITE);
		#else
			::WaitForSingleObject(m_hLoadEvent,INFINITE);
		#endif
		CloseHandle(m_hLoadEvent);
		m_hLoadEvent = 0;
	}
	return m_pdomLive;
};

HRESULT CImplContent::getNewDocument(IXMLDOMDocument ** ppRetDoc)
{
	HRESULT hr = S_OK;
	CDOM3Document oNewDom;
	CComQIPtr<IXMLDOMDocument> pDoc = oNewDom.m_pXMLParser;
	
	if(pDoc) {
		CComQIPtr<IXMLDOMDocument2> pDoc2 = pDoc;

		if(pDoc2) {
			CComPtr<IXMLDOMSchemaCollection> pSchemas;
			hr = getSchemaCache(&pSchemas);

			if(pSchemas) {
				hr = pDoc2->putref_schemas(CComVariant(pSchemas));
			}
		}

		hr = pDoc.QueryInterface(ppRetDoc);
	}

	return hr;
}

HRESULT CImplContent::GetEvaluator(IXPathEvaluator ** ppEvaluator)
{
	HRESULT hr = E_FAIL;
	if(!m_pEvaluatorProxy)
	{
		//initialise the proxy (possibly we should postpone this until first request.)
		m_pEvaluatorProxy->CreateInstance(&m_pEvaluatorProxy);
		// CreateInstance does increase the refcount so we need to 
		// increase it.
		m_pEvaluatorProxy->AddRef();
		m_pEvaluatorProxy->init(this);
	}
	if(m_pEvaluatorProxy)
		hr = m_pEvaluatorProxy->QueryInterface(IID_IXPathEvaluator,(void**)ppEvaluator);
	return hr;
}


void CImplContent::notifyDelete()
{

}


void CImplContent::put_schemaCache(IXMLDOMSchemaCollection *pCache)
{
	m_pSchemaCache = pCache;
	//pCache->AddRef();
}

void CImplContent::onReset()
{
	//does nothing, notification event for 
	//	derived classes to execute specific
	//	behaviour.
}

bool CImplContent::getInstanceBoolean(IXMLDOMNode * pContextNode,BSTR sXPath)
{
	//TODO: this will return false if it could not
	//	resolve the XPath to a bool.  I think it should 
	//	return an error.

#if DEBUG_LEVEL & DEBUG_MONITOR_DATAACCESS
	AtlTrace("'formsplayer.dll': getInstanceBoolean Called\n");
#endif
	//Different to otehr gets, this returns its value rather
	//	than puts it in a n output argument.

	BOOL BoolVal = FALSE;
	CComPtr<IXPathResult> pResult;
	//Get the IXPathResult object based on required XPath and Result type.
	HRESULT hr = getXPathResult
	(
		sXPath,
		pContextNode,
		BOOLEAN_TYPE,
		&pResult
	);
	
	//Get the node and the HRESULT for returning
	if(SUCCEEDED(hr))
		hr = pResult->get_booleanValue(&BoolVal);

	return BoolVal != FALSE;
}
